# vim:ts=4:sw=4:noet:
import unittest
from c.compiler.parse import Parser
from c.compiler.ir import *
from c.base.support import Status

def irmsg(name, recv, args, line=None):
	return (IRMessage, {
		"_line" : line,
		"name"  : name,
		"recv"  : recv,
		"args"  : args
		})

def irname(name, line=None):
	return (IRName, {"_line" : line, "name" : name})

def irint(val, line=None):
	return (IRInteger, {"_line" : line, "value" : val})

def irstr(val, line=None):
	return (IRString, {"_line" : line, "value" : val})

def irblock(stmts, line=None):
	return (IRBlock, {"_line" : line, "statements" : stmts})

def irreturn(val, line=None):
	return (IRReturn, {"_line" : line, "value": val})

def irassign(var, val, line=None):
	return (IRAssign, {"_line" : line, "var": var, "value": val})

class ParserTests(unittest.TestCase):
	def setUp(self):
		self.stat = Status({"exceptions":True})
		self.p = Parser(None, self.stat)

	def checkParse(self, code, expect):
		m = self.p.parseMethodFromSource(code)
		self.checkParseExpect(m, expect)

	def checkParseExpectDict(self, obj, expect):
		for e in expect:
			if e=="_line":
				eline = expect[e]
				if eline:
					if len(eline)==2:
						self.assertEqual(obj.line[:2], eline[:2])
					else:
						self.assertEqual(obj.line, eline)
			else:
				self.assert_(hasattr(obj, e), "%s doesn't have attribute '%s'" % (obj, e))
				item = getattr(obj, e)
				eitem = expect[e]
				self.checkParseExpect(item, eitem)

	def checkParseExpectList(self, obj, expect):
		self.assertEqual(len(obj), len(expect))
		for li, ei in zip(obj, expect):
			self.checkParseExpect(li, ei)

	def checkParseExpectTuple(self, obj, expect):
		self.assert_(isinstance(obj, expect[0]), "Expected class '%s', got '%s'" % (expect[0], obj))
		self.checkParseExpect(obj, expect[1])

	def checkParseExpect(self, obj, expect):
		if isinstance(expect, dict):
			self.checkParseExpectDict(obj, expect)
		elif isinstance(expect, list):
			self.checkParseExpectList(obj, expect)
		elif isinstance(expect, tuple):
			self.checkParseExpectTuple(obj, expect)
		elif isinstance(expect, str):
			self.assertEqual(obj, expect)
		elif isinstance(expect, int):
			self.assertEqual(obj, expect)
		else:
			print "OMG", obj, expect

	def testA(self):
		self.checkParse("""
moo [
^ 3.
]""",
		{"body":{"statements":[
								irreturn(
									irint(3, line=(3,3)),
									line=(3,3)
									)]}})

	def testB(self):
		self.checkParse("""
moo: x [
	x > 1 ifTrue: [ "cheese"
		^ 3.
		"badger"
	].
]""",
		{"body":{"statements":[
								irmsg("ifTrue:", 
										irmsg(">", 
												irname("x", line=(3,3)), 
												[irint(1)],
												line=(3,3)), 
										[irblock([
											irreturn(irint(3, line=(4,4)))
											])],
										line=(3,6))
			]}})

	def testC(self):
		self.checkParse("""
main [
|n|
self penDown.
4 timesRepeat: [
	n := n + 1.
].
]
""",
		{"body":{"statements":[
								irmsg("penDown", irname("self"), []),
								irmsg("timesRepeat:", irint(4), [irblock([
									irassign(
										irname("n"), 
										irmsg(
											"+", 
											irname("n"), 
											[irint(1)], 
											line=(6,6)), 
										line=(6,6))
									])
									])
								]}})

	def testBlockString(self):
		self.checkParse("main [ '[' print. ]", 
										{"body":{"statements":[
															irmsg("print", irstr("["), [])]}})
																	

	def testUnmatchedBlock(self):
		ok = False
		try:
			self.checkParse("main [ [ ]", {})
		except CoreException, e:
			ok = True
			self.assertEqual(e.msg, "Unexpected end of input")
		self.failUnless(ok)
			
	def testUnmatchedBlock2(self):
		ok = False
		try:
			self.checkParse("main [ ] ]", {})
		except CoreException, e:
			ok = True
			self.assertEqual(e.msg, "Expected End of file, got Binary message")
		self.failUnless(ok)


